#include "Acceptor.h"

#include "EventLoop.h"

Acceptor::Acceptor(EventLoop *loop, const InetAddress &addr)
: _loop(loop)
, _addr(addr)
, _acceptSocket()
, _acceptChannel(loop, _acceptSocket.fd())
, _newConnectionCallback()
, _listening(false)
{
    setReuseAddr();
    setReusePort();
    bind();
    _acceptChannel.setReadCallback(std::bind(&Acceptor::handleRead, this));
}

void Acceptor::listen()
{
    _loop->assertInLoopThread();
    _listening = true;
    // _acceptSocket.listen();
    if(::listen(_acceptSocket.fd(), SOMAXCONN) < 0)
    {
        perror("Acceptor::listen");
        std::exit(1);
    }
    _acceptChannel.enableReading();
}

void Acceptor::handleRead()
{
    struct sockaddr_in addr;
    memset(&addr, 0, sizeof(addr));

    socklen_t addrLen = sizeof(addr);
    int connfd = ::accept(_acceptSocket.fd(), (struct sockaddr*)&addr, &addrLen);
    if(connfd < 0)
    {
        perror("Acceptor::handleRead accept");
        return;
    }
    InetAddress peerAddr;
    if(_newConnectionCallback)
    {
        _newConnectionCallback(connfd, peerAddr);
    }
    else
    {
        std::cerr << "No new connection callback set.\n";
        ::close(connfd);
    }
}

void Acceptor::setReuseAddr() {
    int optval = 1;
    if (::setsockopt(_acceptSocket.fd(), SOL_SOCKET, SO_REUSEADDR,
                    &optval, sizeof(optval)) < 0) {
        std::cerr << "setsockopt SO_REUSEADDR failed\n";
    }
}
void Acceptor::setReusePort()
{
    int optval = 1;
    if (::setsockopt(_acceptSocket.fd(), SOL_SOCKET, SO_REUSEPORT,
                    &optval, sizeof(optval)) < 0) {
        std::cerr << "setsockopt SO_REUSEPORT failed\n";
    }
}

void Acceptor::bind()
{
    int ret = ::bind(_acceptSocket.fd(),
                     _addr.getAddrPtr(),
                     static_cast<socklen_t>(sizeof(struct sockaddr_in)));
    if (ret < 0)
    {
        std::cerr << "bind() failed\n";
        ::close(_acceptSocket.fd());
    }
}